package service

import (
	"context"
	"math/rand"
	"strings"

	pb "verify-code/api/verifyCode"
)

type VerifyCodeService struct {
	pb.UnimplementedVerifyCodeServer
}

func NewVerifyCodeService() *VerifyCodeService {
	return &VerifyCodeService{}
}

// 真正的业务逻辑处理函数 需要补充
func (s *VerifyCodeService) GetVerifyCode(ctx context.Context, req *pb.GetVerifyCodeRequest) (*pb.GetVerifyCodeReply, error) {
	return &pb.GetVerifyCodeReply{
		Code: RandCode(int(req.Length), req.Type),
	}, nil
}

// 开放的被调用方法，被调用的方法
func RandCode(l int, t pb.Type) string {
	// 基于类型 t 生成对应的验证码
	switch t {
	case pb.Type_DEFAULT:
		fallthrough
	case pb.Type_DIGIT:
		// return randCode("0123456789", l)
		return randCode("0123456789", l, 4)
	case pb.Type_LETTER:
		// return randCode("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", l)
		return randCode("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", l, 5)
	case pb.Type_MIXED:
		// return randCode("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", l)
		return randCode("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", l, 6)
	default:
		return string("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")[:l]
	}
}

// 随机数的核心方法（优化）
// 一次随机多个随机位
func randCode(chars string, l, idxBits int) string {
	// 计算有效的二进制数位 基于chars的长度
	// 形成掩码 mask 例如使用低六位 00000000 00111111
	idxmask := 1<<idxBits - 1 // 0001000000 - 1 = 0000111111
	// 63 位使用几次
	idxMax := 63 / idxBits

	// 利用string builder构造结果缓冲
	sb := strings.Builder{}
	sb.Grow(l) // 预分配 l 个字符的空间
	// cache 随机位缓存
	// remain 当前可以用几次
	rs := rand.Int63()
	for i, cache, remain := l-1, rs, idxMax; i >= 0; {
		if remain == 0 {
			cache, remain = rand.Int63(), idxMax // 重新生成随机数
		}
		if idx := int(cache & int64(idxmask)); idx < len(chars) {
			sb.WriteByte(chars[idx]) // 将随机的字符写入到结果中
			i--                      // 继续下一个位置
		}
		cache >>= idxBits // 右移 idxBits 位
		remain--          // 使用一次
	}
	return sb.String()
}

// 随机的核心方法 （简单）
// func randCode(chars string, l int) string {
// 	if l <= 0 || len(chars) == 0 {
// 		return ""
// 	}
// 	result := make([]byte, l)
// 	for i := range result {
// 		result[i] = chars[rand.Intn(len(chars))]
// 	}
// 	return string(result)
// }
